package service;


import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import org.bouncycastle.util.encoders.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;

public class SecurityService {

	private PublicKey publicKey = null;
	private PrivateKey privateKey = null;
	private SecretKey secretKey = null;
	private byte[] iv = null;
	private static SecurityService instance = null;
	private static final Object singletonObject = new Object();
	private Mac hmac;


	public SecurityService() {

	}

	public static SecurityService getInstance() {
		if(instance == null) {
			synchronized(singletonObject) {
				if(instance == null) instance = new SecurityService();
			}
		}
		return instance;
	}

	public byte[] generateBase64RandomNumber(int sizeInBytes) {

		SecureRandom secureRandom = new SecureRandom(); 
		byte[] number = new byte[sizeInBytes];
		secureRandom.nextBytes(number);	

		return encodeBase64(number);		
	}

	public byte[] encodeBase64(byte[] message) {

		return Base64.encode(message);
	}

	public byte[] decodeBase64(byte[] message) {

		return Base64.decode(message);
	}

	public byte[] encryptMessage(byte[] message) {
		Cipher crypt = null;
		try {
			crypt = Cipher.getInstance("RSA/NONE/OAEPWithSHA256AndMGF1Padding");
			crypt.init(Cipher.ENCRYPT_MODE, publicKey);
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		byte[] encrypted = null;
		try {
			//System.out.println("code vor encrypt: "+new String(message));
			encrypted = crypt.doFinal(message);
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return encrypted;
	}

	public byte[] decryptMessage(byte[] code) throws BadPaddingException {
		Cipher crypt = null;
		try {
			crypt = Cipher.getInstance("RSA/NONE/OAEPWithSHA256AndMGF1Padding");
			crypt.init(Cipher.DECRYPT_MODE, privateKey);
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		byte[] decrypted = null;
		try {
			//System.out.println("code vor decrypt: "+new String(code));
			decrypted = crypt.doFinal(code);
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return decrypted;

	}



	public void setPublicKey(PublicKey publicKey) {
		this.publicKey = publicKey;
	}

	public void setPrivateKey(PrivateKey privateKey) {
		this.privateKey = privateKey;
	}

	public SecretKey generateSecretKey(int keysize) {
		KeyGenerator generator = null;
		try {
			generator = KeyGenerator.getInstance("AES");
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// KEYSIZE is in bits
		generator.init(keysize);
		SecretKey key = generator.generateKey(); 

		return key;
	}

	public byte[] encryptMessageAES(byte[] message) {
		Cipher cipher = null;
		try {
			cipher = Cipher.getInstance("AES/CTR/NoPadding");
			cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidAlgorithmParameterException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		byte[] result = null;
		try {
			result = cipher.doFinal(message);
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return result;
	}

	public byte[] decryptMessageAES(byte[] code) throws BadPaddingException {
		Cipher cipher = null;
		byte[] result = null;
		if(iv != null && secretKey != null) {
			try {
				cipher = Cipher.getInstance("AES/CTR/NoPadding");
				cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
			} catch (InvalidKeyException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InvalidAlgorithmParameterException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (NoSuchAlgorithmException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (NoSuchPaddingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}


			
			try {
				result = cipher.doFinal(code);
			} catch (IllegalBlockSizeException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		return result;
	}

	public void setSecretKey(SecretKey secretKey) {
		this.secretKey = secretKey;
	}

	public void setIV(byte[] iv) {
		this.iv = iv;
	}

	public void setHMac(Mac hmac) {
		this.hmac = hmac;
	}

	public byte[] generateHMac(String message) {
		hmac.update(message.getBytes());
		byte[] hash = hmac.doFinal();


		return hash;
	}

}
